package io.dy.loop_execute;

import java.util.function.Consumer;
import java.util.function.Function;

/**
 * @author : dmj
 * @date: 2024/7/4 4:57 下午
 * @description: 基于wait-notify 实现多线程顺序(循环)执行 比如打印 abcabcabc
 */
public class WaitNotifyLoopExecute {

    /**
     * 执行标识 用于比对获取执行哪个函数
     */
    private int executeFlag;

    /**
     * 循环次数 默认1次
     */
    private int loopCount;

    /**
     * 执行动作
     * @param currentExecuteFlag 当前执行标识
     * @param nextExecuteFlag 下一步执行标识
     * @param runnable 具体执行函数<无参数无返回值>
     */
    public void execute(int currentExecuteFlag, int nextExecuteFlag, Runnable runnable){
        for(int i = 0; i < loopCount; i++){
            synchronized (this){
                //当前执行标识不等于设置的执行标识 则wait
                while (this.executeFlag != currentExecuteFlag){
                    try {
                        this.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                //等于执行标识，那么开始执行传入的函数
                runnable.run();
                //设置下一个执行标识
                this.executeFlag = nextExecuteFlag;
                //唤醒所有等待线程
                this.notifyAll();
            }
        }
    }

    /**
     * 执行动作
     * @param currentExecuteFlag 当前执行标识
     * @param nextExecuteFlag 下一步执行标识
     * @param consumer 具体执行函数<有参数无返回值>
     * @param param 参数
     * @param <T>
     */
    public <T> void execute(int currentExecuteFlag, int nextExecuteFlag, Consumer<T> consumer, T param){
        for(int i = 0; i < loopCount; i++){
            synchronized (this){
                //当前执行标识不等于设置的执行标识 则wait
                while (this.executeFlag != currentExecuteFlag){
                    try {
                        this.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                //等于执行标识，那么开始执行传入的函数
                consumer.accept(param);
                //设置下一个执行标识
                this.executeFlag = nextExecuteFlag;
                //唤醒所有等待线程
                this.notifyAll();
            }
        }
    }

    /**
     * 执行动作
     * @param currentExecuteFlag 当前执行标识
     * @param nextExecuteFlag 下一步执行标识
     * @param function 具体执行函数<有参数有返回值>
     * @param req 参数
     * @param <Req>
     * @param <Res>
     * @return
     */
    public <Req, Res> Res execute(int currentExecuteFlag, int nextExecuteFlag, Function<Req, Res> function, Req req){
        for(int i = 0; i < loopCount; i++){
            synchronized (this){
                //当前执行标识不等于设置的执行标识 则wait
                while (this.executeFlag != currentExecuteFlag){
                    try {
                        this.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                //等于执行标识，那么开始执行传入的函数
                Res res = function.apply(req);
                //设置下一个执行标识
                this.executeFlag = nextExecuteFlag;
                //唤醒所有等待线程
                this.notifyAll();
                return res;
            }
        }
        return null;
    }

    public WaitNotifyLoopExecute(int executeFlag){
        new WaitNotifyLoopExecute(executeFlag, 1);
    }

    public WaitNotifyLoopExecute(int executeFlag, int loopCount){
        this.executeFlag = executeFlag;
        this.loopCount = loopCount;
    }
}
